#include"PageCache.h"
#include"Common.h"

PageCache PageCache::_sInst;

Span* PageCache::NewSpan(size_t k)
{
    assert(k > 0);

    if(k > NSPANLIST - 1)
    {
        void* ptr = SystemAlloc(k);
        //Span* span = new Span;
        Span* span = _spanPool.New();
        span->Page_Id = (Page_Id)ptr >> PAGE_SHIFT;
        span->_n = k;

        //_idSpanMap[span->Page_Id] = span;
        _idSpanMap.set(span->Page_Id,span);
        
        return span;
    }

    //先检查第k号桶里面有没有
    if(!_spanlists[k].Empty())
    {
        Span* kSpan = _spanlists[k].Popfront();

        //建立id和kspan 的映射，方便centralcache回收内存的时候，查找相应的span
        for(Page_Id i = 0;i < kSpan->_n;i++)
        {
            //_idSpanMap[kSpan->Page_Id + i] = kSpan;
            _idSpanMap.set(kSpan->Page_Id,kSpan);

        }

        return kSpan;
    }

    //检查后面的桶有木有span，如果有就切
    for(size_t i = k+1;i < NSPANLIST;i++)
    {
        if(!_spanlists[k].Empty())
        {
            Span* nspan = _spanlists[i].Popfront();
            //Span* kspan = new Span;
            Span* kspan = _spanPool.New();

            //在nspan的头部切一个k页下来
            //k页返回   剩下的挂起来
            kspan->Page_Id = nspan->Page_Id;
            kspan->_n = k;

            nspan->Page_Id += k;
            nspan->_n -= k;

            _spanlists[nspan->_n].Pushfront(nspan);

             //存储nspan的首尾页号跟nspan映射，方便pagecache回收内存
            //_idSpanMap[nspan->Page_Id] = nspan;
            //_idSpanMap[nspan->Page_Id + nspan->_n] = nspan;
            _idSpanMap.set(nspan->Page_Id,nspan);
            _idSpanMap.set(nspan->Page_Id + nspan->_n,nspan);

            //建立id和kspan 的映射，方便centralcache回收内存的时候，查找相应的span
            for(Page_Id i = 0;i < kspan->_n;i++)
            {
                //_idSpanMap[kspan->Page_Id + i] = kspan;
                _idSpanMap.set(kspan->Page_Id + i,kspan);
            }
            return kspan;
        }
    }

    //走到这个位置说明没有大块内存了，需要向堆申请空间
    //Span* bigspan = new Span;
    Span* bigspan = _spanPool.New();
    void* ptr = SystemAlloc(NSPANLIST - 1);
    bigspan->Page_Id = (Page_Id)ptr >> PAGE_SHIFT;
    bigspan->_n = NSPANLIST - 1;

    _spanlists[bigspan->_n].Pushfront(bigspan);

    return NewSpan(k);
}


Span* PageCache::MapObjectToSpan(void* obj)
{
    Page_Id id = ((Page_Id)obj >> PAGE_SHIFT);
    // std::unique_lock<std::mutex>lock(_PageMTX);
    // auto ret = _idSpanMap.find(id);
    // if(ret != _idSpanMap.end())
    // {
    //     return ret->second;
    // }
    // else
    // {
    //     assert(false);
    //     return nullptr;
    // }

    auto ret = (Span*)_idSpanMap.get(id);
    assert(ret != nullptr);
    return ret;
}

void PageCache::ReleaseSpanToPageCache(Span* span)
{
    //大于128页的直接还给堆
    if(span->_n > NSPANLIST - 1)
    {
        void* ptr = (void*)(span->Page_Id << PAGE_SHIFT);
        //这里的第二个参数不是很清楚，后面得重新调试看看是啥情况
        SystemFree(ptr,span->_n);

        //delete span;
        _spanPool.Delete(span);
        return;
    }

    //对前后的页尝试合并
    while(1)
    {   
        //向前合并
        Page_Id prevId = span->Page_Id - 1;
        // auto ret = _idSpanMap.find(prevId);
        // //前面没有页号了
        // if(ret == _idSpanMap.end())
        // {
        //     break;
        // }
        auto ret = (Span*)_idSpanMap.get(prevId);
        if(ret == nullptr)
        {
            break;
        }

        Span* prevspan = ret;
        //前面有页号在使用
        if(prevspan->_isUse == true)
        {
            break;
        }

        //页号之和大于128
        if(prevspan->_n + span->_n > NSPANLIST - 1)
        {
            break;
        }

        span->Page_Id = prevspan->Page_Id;
        span->_n += prevspan->_n;

        _spanlists[prevspan->_n].Erase(prevspan);

        //delete prevspan;
        _spanPool.Delete(prevspan);
    }

    while(1)
    {
        //向后合并
        Page_Id nextId = span->Page_Id + span->_n;
        // auto ret = _idSpanMap.find(nextId);
        //后面没有页号了
        // if(ret == _idSpanMap.end())
        // {
        //     break;
        // }
        auto ret = (Span*)_idSpanMap.get(nextId);
        if(ret == nullptr)
        {
            break;
        }
        Span* nextspan = ret;

        //后面页正在使用
        if(nextspan->_isUse == true)    
        {
            break;
        }

        //页号之和大于128
        if(nextspan->_n + span->_n > NSPANLIST - 1)
        {
            break;
        }
        
        span->_n += nextspan->_n;

        _spanlists[nextspan->_n].Erase(nextspan);

        //delete nextspan;
        _spanPool.Delete(nextspan);

    }

    _spanlists[span->_n].Pushfront(span);
    span->_isUse = true;   
    //_idSpanMap[span->Page_Id] = span;
    //_idSpanMap[span->Page_Id + span->_n - 1] = span;
    _idSpanMap.set(span->Page_Id,span);
    _idSpanMap.set(span->Page_Id + span->_n,span);
}
